package com.zenghus.spring.formework.webmvc.servlet;

import com.zenghus.spring.formework.annotation.Controller;
import com.zenghus.spring.formework.annotation.RequestMapping;
import com.zenghus.spring.formework.annotation.RequestParam;
import com.zenghus.spring.formework.aop.AopProxy;
import com.zenghus.spring.formework.aop.AopProxyUtils;
import com.zenghus.spring.formework.context.ApplicationContext;
import com.zenghus.spring.formework.webmvc.HandlerAdapter;
import com.zenghus.spring.formework.webmvc.HandlerMapping;
import com.zenghus.spring.formework.webmvc.ModelAndView;
import com.zenghus.spring.formework.webmvc.ViewResolver;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zenghu
 * @date 2018/4/28 11:32
 */
public class DispatchServlet extends HttpServlet{

    private final String LOCATION  = "contextConfigLocation";

    //spring支持正则，
    private List<HandlerMapping> handlerMappings=new ArrayList<HandlerMapping>();

    //存放方法对于的参数index
    private Map<HandlerMapping,HandlerAdapter> handlerAdapters=new HashMap<HandlerMapping, HandlerAdapter>();

    private List<ViewResolver> viewResolvers=new ArrayList<ViewResolver>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub

        //初始化ioc
        ApplicationContext ap=new ApplicationContext(config.getInitParameter(LOCATION));

        //初始化mvc
        initStrategies(ap);

    }

    //加载组件（springMVC的9大组件）
    private void initStrategies(ApplicationContext context) {

        initMultipartResolver(context);//文件上传
        initLocaleResolver(context);//本地化解析
        initThemeResolver(context);//主题解析

        //自己实现（通过HandlerMapping将请求映射到相应的处理器，将url和method对应）
        initHandlerMappings(context);
        //自己实现(通过HandlerAdapter进行多类型的参数动态匹配包括类型转换，动态赋值)
        initHandlerAdapters(context);
        //自己实现（通过viewResolver解析逻辑视图到具体视图实现
        initViewResolvers(context);

        initHandlerExceptionResolvers(context);//异常解析器
        initRequestToViewNameTranslator(context);//直接解析请求到视图名
        initFlashMapManager(context);//flash映射管理器
    }

    //通过viewResolver解析逻辑视图到具体视图实现
    private void initViewResolvers(ApplicationContext context) {
        //解决页面名字和模板文件关联的问题
        String templateRoot=context.getConfig().getProperty("templateRoot");
        String templateRootPath=this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir=new File(templateRootPath);
        for (File template:templateRootDir.listFiles()) {
            this.viewResolvers.add(new ViewResolver(template.getName(),template));
        }
    }

    //方法的参数实现动态的配置
    private void initHandlerAdapters(ApplicationContext context) {
        //在初始化阶段，能把参数的名称和类型按照一定的顺序保存下来
        //用反射调用的时候参数是一个数组
        //可通过记录参数的位置，挨个从数组中添值。这样就可以和参数的顺序无关了
        for (HandlerMapping handler:this.handlerMappings) {
            //没给方法有一个参数列表，这里是参数的形参key位参数name，value为参数的index
            Map<String,Integer> param=new HashMap<String, Integer>();

            //这里处理了命名参数
            Annotation[][] ha= handler.getMethod().getParameterAnnotations();
            for(int i=0;i<ha.length;i++){
                for (Annotation a:ha[i]){
                    if(a instanceof RequestParam){
                        String paramName=((RequestParam) a).value();
                        if(!"".equals(paramName.trim())){
                            param.put(paramName,i);
                        }
                    }
                }
            }

            //处理非命名参数
            Class<?>[] paramTypes=handler.getMethod().getParameterTypes();
            for(int i=0;i<paramTypes.length;i++){
                Class<?> type=paramTypes[i];
                if(type==HttpServletRequest.class || type==HttpServletResponse.class){
                    param.put(type.getName(),i);
                }
            }
            this.handlerAdapters.put(handler,new HandlerAdapter(param));
        }

    }

    //将url和method对应
    private void initHandlerMappings(ApplicationContext context) {
        //这里应该是一个map，key应该是url,value应该是method

        //从容器中取出所有的实例
        String[] beannames=context.getBeanDefinittionNames();
        try{

            for (String beanname:beannames) {
                //到了mvc层，对外提供的方法只有一个getBean方法
                //返回的对象不是BeanWrapper.通过工具类找到原始对象
                Object proxy=context.getBean(beanname);
                Object instance= AopProxyUtils.get(proxy);

                Class<?> clazz=instance.getClass();
                //不是所有的类都是controller
                if(!clazz.isAnnotationPresent(Controller.class)){}

                String baseurl="";

                if(clazz.isAnnotationPresent(RequestMapping.class)){
                    RequestMapping requestMapping=clazz.getAnnotation(RequestMapping.class);
                    baseurl=requestMapping.value();
                }

                Method[] methods=clazz.getMethods();
                for (Method method:methods) {
                    if(method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping requestMapping=method.getAnnotation(RequestMapping.class);
                        String regex=("/"+baseurl+requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
                        Pattern pattern=Pattern.compile(regex);
                        this.handlerMappings.add(new HandlerMapping(pattern,instance,method));
                        System.out.println(regex+"\t"+clazz.getName()+"\t"+method.getName());
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private void initFlashMapManager(ApplicationContext context) {}

    private void initThemeResolver(ApplicationContext context) {}

    private void initLocaleResolver(ApplicationContext context) {}

    private void initMultipartResolver(ApplicationContext context) {}

    private void initRequestToViewNameTranslator(ApplicationContext context) {}

    private void initHandlerExceptionResolvers(ApplicationContext context) {}

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req, resp);
    }

    private void doDispatch(HttpServletRequest req,HttpServletResponse resp){
        try{
            //根据url找到对应method
            HandlerMapping handlerMapping= getHadnler(req);
            if(handlerMapping==null){
                resp.getWriter().write("404 Exception,details:\r\n");
            }

            HandlerAdapter handlerAdapter=getHandlerAdpter(handlerMapping);

            ModelAndView modelAndView=handlerAdapter.handler(req,resp,handlerMapping);

            processDispatchResult(resp,modelAndView);

        }catch (Exception e){
            try {
                resp.getWriter().write("500 Execption,detaions:\r\n"+ e.getMessage());
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

    }

    private void processDispatchResult(HttpServletResponse resp, ModelAndView modelAndView) throws IOException {
        //调用viewResolver的resolveView方法
        if(null==modelAndView){ return; }

        if(this.viewResolvers.isEmpty()){return;}

        for (ViewResolver viewResolver:this.viewResolvers){
            if(!viewResolver.getViewName().equals(modelAndView.getViewName())){continue; }

            String out=viewResolver.viewResolver(modelAndView);
            if(out!=null){
                resp.getWriter().write(out);
            }

        }
    }

    private HandlerAdapter getHandlerAdpter(HandlerMapping handlerMapping) {
        if(this.handlerAdapters.isEmpty()){ return null;}
        return this.handlerAdapters.get(handlerMapping);
    }

    private HandlerMapping getHadnler(HttpServletRequest req) {
        if(this.handlerMappings.isEmpty()){ return null;}

        String url=req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath,"").replaceAll("/+","/");
        for (HandlerMapping handler:this.handlerMappings) {
            //正则匹配url
            Matcher matcher=handler.getPattern().matcher(url);
            if(!matcher.matches()){ continue;}
            return handler;
        }

        return null;
    }
}
